/* ************************************************************************

   Copyright:

   License:

   Authors: SALMON Charles

************************************************************************ */

/**
 * Extend the qooxdoo Json store to adapt it to our JSON-RPC layer
 */
qx.Class.define("bip.rpc.Json", 
{
	extend : qx.data.store.Json,
	
	include : [qx.locale.MTranslation],
	
	statics :
  {
    /**
     * Serializes the properties of the given qooxdoo object into a native
     * object.
     *
     * @param object {qx.core.Object}
     *   Any qooxdoo object
     *
     * @param qxSerializer {Function}
     *   Function used for serializing qooxdoo objects stored in the propertys
     *   of the object. Check for the type of classes you want to serialize
     *   and return the serialized value. In all other cases, just return
     *   nothing.
     * @param dateFormat {qx.util.format.DateFormat} If a date formater is given,
     *   the format method of this given formater is used to convert date
     *   objects into strings.
     * @return {String}
     *   The serialized object.
     */
    toNativeObject : function(object, qxSerializer, dateFormat)
    {
      var result;

      // null or undefined
      if (object == null)
      {
        return null;
      }

      // data array
      if (qx.Class.hasInterface(object.constructor, qx.data.IListData))
      {
        result = {"javaClass":"java.util.ArrayList","list":[]};  // Specific to the JSON omplementation used
        for (var i = 0; i < object.getLength(); i++)
        {
          result["list"].push(bip.rpc.Json.toNativeObject(
            object.getItem(i), qxSerializer, dateFormat)
          );
        }

        return result;
      }

      // other arrays
      if (qx.lang.Type.isArray(object))
      {
        result = [];
        for (var i = 0; i < object.length; i++)
        {
          result.push(bip.rpc.Json.toNativeObject(
            object[i], qxSerializer, dateFormat)
          );
        }

        return result;
      }

      // qooxdoo object
      if (object instanceof qx.core.Object)
      {
        if (qxSerializer != null)
        {
          var returnValue = qxSerializer(object);

          // if we have something returned, return that
          if (returnValue != undefined)
          {
            return returnValue;
          }

          // continue otherwise
        }

        result = {};

        var properties =
          qx.util.PropertyUtil.getAllProperties(object.constructor);

        for (var name in properties)
        {
          // ignore property groups
          if (properties[name].group != undefined)
          {
            continue;
          }

          var value = object["get" + qx.lang.String.firstUp(name)]();
          result[name] = bip.rpc.Json.toNativeObject(
            value, qxSerializer, dateFormat
          );
        }
        
        if (! result["javaClass"]) {
        	return {"map":result,"javaClass":"java.util.HashMap"}; // Specific to the JSON omplementation used
        }

        return result;
      }

      // date objects with date format
      if (qx.lang.Type.isDate(object) && dateFormat != null) {
        return dateFormat.format(object);
      }

      // localized strings
      if (object instanceof qx.locale.LocalizedString) {
        return object.toString();
      }

      // JavaScript objects
      if (qx.lang.Type.isObject(object))
      {
        result = {};

        for (var key in object)
        {
          result[key] = bip.rpc.Json.toNativeObject(
            object[key], qxSerializer, dateFormat
          );
        }

        return result;
      }

      // all other stuff, including String, Date, RegExp
      return object;
    }
		
  },
	
	events :
  {
    /**
     * Data event fired after the model has been created. The data will be the
     * created model.
     */
    "loaded" : "qx.event.type.Data",

    /**
     * Fired when an error (aborted, timeout or failed) occurred
     * during the load. 
     * The date is the error
     */
    "error" : "qx.event.type.Data",
    
    /**
     * Fired at the beginning of a load
     * The data is the description of the action
     */
    "loading" : "qx.event.type.Data"
    
  },
  
  properties :
  {
    /**
     * Property for holding the posted model instance.
     */
    postModel : {
      nullable: true,
      event: "changePostModel"
    }
  },
	
	members :
	  {
		/**
	     * Creates and sends a JSON-RPC request. Additionally two
	     * listeners will be added for the state and the completed event of the
	     * request.
	     *
	     * @param url {String} The url for the request.
	     */
	    _createRequest: function(url) {
	    	var self = this;
	      // check for the request configuration hook
	      var del = this._delegate;
	      if (del && qx.lang.Type.isFunction(del.configureRequest)) {
	        this._delegate.configureRequest(this);
	      }
	      if (url == null) {return;}
	      /*if ((qx.core.Environment.get("qx.debug")))
	      {
	    	  var url = qx.util.ResourceManager.getInstance().toUri("resource/data/"+url+".json");
	          var store = new qx.data.store.Json(url);
	          store.bind("model",this,"model");
	          return;
	      }*/
	      // Post topic
	      if (url.match("topics/postTopic")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Posting topic"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().TopicManager.postTopic(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			bip.rpc.Json.toNativeObject(this.getPostModel())
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Remove topic
	      else if (url.match("topics/removeTopic")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Removing topic"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().TopicManager.removeTopic(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			this.getPostModel().getIdTopic(),this.getPostModel().getIdLocal()
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Get all topics
	      else if (url.match("topics/getAllTopics")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Fetching topics"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().TopicManager.getAllTopics(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);}
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Get a survey
	      else if (url.match("survey/fetchSurvey")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Fetching survey"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().SurveyManager.fetchSurvey(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			this.getPostModel()
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Post a survey
	      else if (url.match("survey/postSurvey")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Saving survey"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().SurveyManager.postSurvey(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			bip.rpc.Json.toNativeObject(this.getPostModel())
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Get the survey tree model
	      else if (url.match("survey/getSurveyTree")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Getting survey"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().SurveyManager.getSurveyTree(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);}
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Get all questions of a topic
	      else if (url.match("questions/getQuestionsOfTopic")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Fetching questions"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().QuestionManager.getQuestionsOfTopic(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			this.getPostModel().getIdTopic(),this.getPostModel().getIdLocal()
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Post a question
	      else if (url.match("questions/postQuestion")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Posting question"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().QuestionManager.postQuestion(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			bip.rpc.Json.toNativeObject(this.getPostModel())
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Remove question
	      else if (url.match("questions/removeQuestion")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Removing question"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().QuestionManager.removeQuestion(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			this.getPostModel().getIdQuestion(), this.getPostModel().getIdLocal()
			    			
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      // Create a new user
	      else if (url.match("user/createUser")) {
	      	try {
	      		this.fireDataEvent("loading",this.tr("Creating user"));
	      		bip.rpc.RpcManager.getInstance().getJsonRpc().UserManager.createUser(
			    			function(result,error) {self.__jsonCompleteHandler.apply(self,arguments);},
			    			this.getPostModel().getName(), this.getPostModel().getPassword()
			    	);
	      	} catch(e) {
	      		alert("JSON-RPC problem:"+e);
	      	}
	      } 
	      else {
	      	alert("ERROR: Unknown json-rpc request:"+url);
	      }
	    },
	    
	    /**
	     * Handler for the completion of the requests. It invokes the creation of
	     * the needed classes and instances for the fetched data using
	     * {@link qx.data.marshal.Json}.
	     *
	     * @param res {Map} The result 
	     */
	    __jsonCompleteHandler : function(res, e)
	    {
	    	if (res == null) {
	    		this.setState("failed");
		      var exception = new bip.rpc.RpcException();
		      exception.setName(e.name);
		      exception.setCode(e.code);
		      exception.setMessage(e.message);
		      //exception.setTrace(e.javaStack);
		      this.fireDataEvent("error",exception);
				  return;
	    	}
	    	this.setState("completed");
	    	
	    	var data = this.__formatResult(res);
	    	//if (res["javaClass"] && res["list"]) {data = res.list;}  
	    	// check for the data manipulation hook
	    	var del = this._delegate;
	    	if (del && qx.lang.Type.isFunction(del.manipulateData)) {
	    		data = this._delegate.manipulateData(data);
	    	}

	    	// create the class
	    	this._marshaler.toClass(data, true);

	    	var oldModel = this.getModel();
	    	
	    	this.setModel(null);  // DO NOT REMOVE !!
	    	        							// Force full widget content rebuilding (like list) 
	    	// set the initial data
	    	this.setModel(this._marshaler.toModel(data));

	    	// get rid of the old model
	    	if (oldModel && oldModel.dispose) {
	    		oldModel.dispose();
	    	}

	    	// fire complete event
	    	this.fireDataEvent("loaded", this.getModel());
	    },
	    
	    // The json implementation returns a 
	    //    {map:jsonMap, javaClass:"..."} when encoding a java Map and
	    //    {list:jsonList, javaClass:"..."} when encoding a java List
	    // The __format result remove theses un-necessaries encapsulations
	    __formatResult: function(res) {
	    	var data = res;
		  	if (res != null && res["javaClass"] && res["list"]) {
		  		data = [];
		  		for(var i=0;i<res.list.length;i++) {
		  			data[i] = this.__formatResult(res.list[i]);
		  		}
		  	} 
		  	else if (res != null && res["javaClass"] && res["map"]) {
		  		data= {};
		  		for(var key in res.map) {
		  			data[key] = this.__formatResult(res.map[key]);
		  		}
		  	} 
		  	else if (res != null && typeof res == "object") {
		  		data= {};
		  		for(var key in res) {
		  			data[key] = this.__formatResult(res[key]);
		  		}
		  	}
		  	
		  	
		  	return data;
		  }
	  }
  
});